package com.atguigu.gmall.item.service.impl;

import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.item.feign.GmallPmsClient;
import com.atguigu.gmall.item.feign.GmallSmsClient;
import com.atguigu.gmall.item.feign.GmallWmsClient;
import com.atguigu.gmall.item.service.ItemService;
import com.atguigu.gmall.item.vo.ItemVO;
import com.atguigu.gmall.pms.entity.*;
import com.atguigu.gmall.pms.vo.ItemGroupVO;
import com.atguigu.gmall.pms.vo.ItemSaleAttrVO;
import com.atguigu.gmall.sms.vo.ItemSaleVO;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Service
public class ItemServiceImpl implements ItemService {
    @Resource
    GmallPmsClient pmsClient;
    @Resource
    GmallWmsClient wmsClient;
    @Resource
    GmallSmsClient smsClient;
    @Resource
    ThreadPoolExecutor executor;
    /*
        计算(cpu)密集型:
        io密集型:
            当前服务发起了N多个远程访问，读取的是其他服务返回的数据
        优化：
            1、缓存：不合适    数据量不是特别大的高频访问数据
                缓存skuId对应的商品详情到redis中
                如果有1亿的商品，每个商品详情信息 0.5kb(512byte)一个
                0.5kb*1亿
            2、异步：
                mq不合适：代码无法解耦合
                多线程：
                    每个异步请求使用一个子线程处理(远程访问多 属于io密集型操作 可以多配置线程)
                    实现方式：
                        继承Thread/实现Runnable/Callable/线程池
                        java是单继承、Runnable无法抛出子线程的异常返回子线程的结果
                        Callable： 可以返回子线程结果 抛出子线程异常
                        线程池：可以维护线程
                需求：需要使用多线程，并且需要获取子线程执行后的结果交给其他线程使用
                        所以可以使用Callable，但是线程又需要维护
                        java8提供了多线程的异步编排：可以按照我们的需求组合多个线程按照先后依赖关系执行
                            并行或者串行
     */
    @Override
    public ItemVO queryItemBySkuId(Long skuId) {
        ItemVO itemVO = new ItemVO();
        //查询itemVo需要的数据 设置给该对象
        //1、查询sku表数据：
        CompletableFuture<SkuEntity> c1 = CompletableFuture.supplyAsync(() -> {
            ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(skuId);
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            if (skuEntity == null) {
                return null;
            }
            itemVO.setSkuId(skuId);
            itemVO.setPrice(skuEntity.getPrice());
            itemVO.setTitle(skuEntity.getTitle());
            itemVO.setSubTitle(skuEntity.getSubtitle());
            itemVO.setWeight(skuEntity.getWeight());
            itemVO.setDefaultImage(skuEntity.getDefaultImage());
            return skuEntity;
        }, executor);

        //2、根据skuId查询sku的图片列表：
        CompletableFuture<Void> c2 = CompletableFuture.runAsync(() -> {
            ResponseVo<List<SkuImagesEntity>> skuImagesResponseVo = pmsClient.querySkuImagesBySkuId(skuId);
            List<SkuImagesEntity> skuImagesEntities = skuImagesResponseVo.getData();
            if (!CollectionUtils.isEmpty(skuImagesEntities)) {
                itemVO.setImages(skuImagesEntities);
            }
        }, executor);
        //3、根据skuId查询sms中sku的营销数据：
        CompletableFuture<Void> c3 = CompletableFuture.runAsync(() -> {
            ResponseVo<List<ItemSaleVO>> skuSalesResponseVo = smsClient.querySkuSalesBySkuId(skuId);
            List<ItemSaleVO> itemSaleVOS = skuSalesResponseVo.getData();
            if (!CollectionUtils.isEmpty(itemSaleVOS)) {
                itemVO.setSales(itemSaleVOS);
            }
        }, executor);
        //4、根究skuId查询wms中sku的库存数据:
        CompletableFuture<Void> c4 = CompletableFuture.runAsync(() -> {
            ResponseVo<List<WareSkuEntity>> wareSkusResponseVo = wmsClient.listBySkuId(skuId);
            List<WareSkuEntity> wareSkuEntities = wareSkusResponseVo.getData();
            if (!CollectionUtils.isEmpty(wareSkuEntities)) {
                itemVO.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock()
                        - wareSkuEntity.getStockLocked() > 0));
            }
        }, executor);
        //5、根据skuId查询sku_attr_value: 页面默认选中sku的销售属性值
        // Map<Long,String>   map.put(3,"黑色") map.put(4,"8G")
        CompletableFuture<Void> c5 = CompletableFuture.runAsync(() -> {
            ResponseVo<Map<Long, String>> mapResponseVo = pmsClient.saleAttrValues(skuId);
            Map<Long, String> map = mapResponseVo.getData();
            if (!CollectionUtils.isEmpty(map)) {
                itemVO.setSkuAttrs(map);
            }
        }, executor);
        //6、根据sku的品牌id查询品牌数据:需要在c1执行结束后执行 串行
        CompletableFuture<Void> c6 = c1.thenAcceptAsync((skuEntity) -> {
            ResponseVo<BrandEntity> brandEntityResponseVo = pmsClient.queryBrandById(skuEntity.getBrandId());
            BrandEntity brandEntity = brandEntityResponseVo.getData();
            if (brandEntity != null) {
                itemVO.setBrandId(brandEntity.getId());
                itemVO.setBrandName(brandEntity.getName());
            }
        }, executor);
        //7、根据sku的三级分类id查询123级分类对象集合:需要在c1执行结束后执行 串行
        CompletableFuture<Void> c7 = c1.thenAcceptAsync(skuEntity -> {
            ResponseVo<List<CategoryEntity>> level123catesResponseVo = pmsClient.queryLevel123cates(skuEntity.getCategoryId());
            List<CategoryEntity> categoryEntities = level123catesResponseVo.getData();
            if (!CollectionUtils.isEmpty(categoryEntities)) {
                itemVO.setCategories(categoryEntities);
            }
        }, executor);

        //8、根据sku的spuid查询spu
        CompletableFuture<Void> c8 = c1.thenAcceptAsync(skuEntity -> {
            ResponseVo<SpuEntity> spuEntityResponseVo = pmsClient.querySpuById(skuEntity.getSpuId());
            SpuEntity spuEntity = spuEntityResponseVo.getData();
            if (spuEntity != null) {
                itemVO.setSpuId(spuEntity.getId());
                itemVO.setSpuName(spuEntity.getName());
            }
        }, executor);
        //9、根据sku的spuid查询spu的图片列表
        CompletableFuture<Void> c9 = c1.thenAcceptAsync(skuEntity -> {
            ResponseVo<SpuDescEntity> spuDescEntityResponseVo = pmsClient.querySpuDescById(skuEntity.getSpuId());
            SpuDescEntity spuDescEntity = spuDescEntityResponseVo.getData();
            if (spuDescEntity != null) {
                itemVO.setSpuImages(Arrays.asList(spuDescEntity.getDecript()
                        .split(",")));
            }
        }, executor);

        //10、根据sku的spuid查询它下面所有的sku的销售属性和值
        //[{attrId:3,attrName:'机身颜色',attrValues:['白色','金色'] },{}]
        CompletableFuture<Void> c10 = c1.thenAcceptAsync(skuEntity -> {
            ResponseVo<List<ItemSaleAttrVO>> itemSaleAttrVosResponseVo = pmsClient.querySkuattrvaluesBySpuId(skuEntity.getSpuId());
            List<ItemSaleAttrVO> itemSaleAttrVOS = itemSaleAttrVosResponseVo.getData();
            if (!CollectionUtils.isEmpty(itemSaleAttrVOS)) {
                itemVO.setSaleAttrs(itemSaleAttrVOS);
            }
        }, executor);
        //11、根据sku的spuid查询它下面所有的sku的 每个sku的销售属性值字符串 映射 属性id
        //将来用户在页面中会选择不同的销售属性值组合 可以跳转到对应的sku详情页面
        //[{'白色,12G,256G': '1624314367975636993'}]
        CompletableFuture<Void> c11 = c1.thenAcceptAsync(skuEntity -> {
            ResponseVo<String> skuJsonsResponseVo = pmsClient.querySkuJsonsBySpuId(skuEntity.getSpuId());
            String skuJsons = skuJsonsResponseVo.getData();
            if (!StringUtils.isEmpty(skuJsons)) {
                itemVO.setSkuJsons(skuJsons);
            }
        }, executor);
        //12、 查询规格参数....
        // 根据categoryId查询属性分组
        // 根据分组查询组内的属性
        // 根据spuid和skuid查询当前商品属性值
        CompletableFuture<Void> c12 = c1.thenAcceptAsync(skuEntity -> {
            ResponseVo<List<ItemGroupVO>> itemGroupsResponseVo = pmsClient.withattrsGroup(skuEntity.getCategoryId(), skuEntity.getSpuId(),
                    skuEntity.getId());
            List<ItemGroupVO> itemGroupVOS = itemGroupsResponseVo.getData();
            if (!CollectionUtils.isEmpty(itemGroupVOS)) {
                itemVO.setGroups(itemGroupVOS);
            }
        }, executor);
        //等待上面所有异步任务执行结束：itemVo对象的属性值才会设置成功 才可以返回
        CompletableFuture.allOf(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12).join();
        //查询数据成功：第一次查询成功，构建静态化页面保存到nginx的静态化页面目录下：F:/html/skuId.html
        System.out.println(itemVO);
        return itemVO;
    }
    //整合thymeleaf后 会自动向容器中添加该对象
    @Resource
    TemplateEngine templateEngine;
    public void asyncCreateHtml(ItemVO itemVO) {
       executor.execute(()->{
           try {
               //参数1： 静态话页面的模板名称(由thymeleaf视图解析器解析)
               String template = "item";
               //参数2：thymleaf的上下文对象，用来给模板页面提供数据
               Context context = new Context();
               context.setVariable("itemVo" , itemVO);
               //参数3：输出流对象，持久化保存静态化页面
               PrintWriter printWriter = new PrintWriter(new File("F:/html/"+itemVO.getSkuId()+".html"));
               templateEngine.process(template , context , printWriter);
           } catch (Exception e) {
//               e.printStackTrace();
           }
       });

    }
}
